import java.util.Arrays;
import java.util.List;
import java.util.regex.*;

public class StringDemo {
    // 字符串连接
    protected static void stringJoin() {
        String part1 = "七律·长征";
        String part2 = "毛泽东";
        String part3 = "1935年10月";
        String part4 = "红军不怕远征难，万水千山只等闲。";
        String part5 = "五岭逶迤腾细浪，乌蒙磅礴走泥丸。";
        String part6 = "金沙水拍云崖暖，大渡桥横铁索寒。";
        String part7 = "更喜岷山千里雪，三军过后尽开颜。";
        String all = part1+"\n"+part2+"\n"+part3+"\n"+part4+"\n"+part5+"\n"+part6+"\n"+part7;
        System.out.println(all);

        //StringBuilder 类在 Java 5 中被提出，它和 StringBuffer 之间的最大不同在于 StringBuilder 的方法不是线程安全的（不能同步访问）。
        //由于 StringBuilder 相较于 StringBuffer 有速度优势
        StringBuffer sb = new StringBuffer(256);
        sb.append(part1).append("\n").append(part2).append("\n").append(part3).append("\n").append(part4).append("\n").append(part5).append("\n").append(part6).append("\n").append(part7);
        System.out.println("using StringBuffer");
        System.out.println(sb);
    }

    // 字符串数组连接(components) 返回字符串
    protected static void array2String() {
        String month[] = {
                "January",
                "February",
                "March",
                "April",
                "May",
                "June",
                "July",
                "August",
                "September",
                "October",
                "November",
                "December",
        };
        System.out.println("array2String, using String.join: " + String.join("--", month));

        List<String> monthArr = Arrays.asList(month);
        System.out.println("List2String, using String.join: " + String.join("--", monthArr));

    }

    // 字符串分割(split) 返回字符串数组
    protected static void string2Array() {
        String str="January--February--March--April--May--June--July--August--September--October--November--December";
        String[] arrayFromSplit  = str.split("--");
        for(int i=0; i<arrayFromSplit.length; ++i){
            System.out.println("string2Array " + i + ", " + arrayFromSplit[i]);
        }
    }

    // 字符串简单查询替换
    protected static void subStringSearchReplace() {
        String ostr = "浔阳江头夜送客，枫叶荻花秋瑟瑟。主人下马客在船，举酒欲饮无管弦。醉不成欢惨将别，别时茫茫江浸月。忽闻水上琵琶声，主人忘归客不发。寻声暗问弹者谁，琵琶声停欲语迟。移船相近邀相见，添酒回灯重开宴。千呼万唤始出来，犹抱琵琶半遮面。转轴拨弦三两声，未成曲调先有情。弦弦掩抑声声思，似诉平生不得志。低眉信手续续弹，说尽心中无限事。轻拢慢捻抹复挑，初为《霓裳》后《六幺》。大弦嘈嘈如急雨，小弦切切如私语。嘈嘈切切错杂弹，大珠小珠落玉盘。间关莺语花底滑，幽咽泉流冰下难。冰泉冷涩弦凝绝，凝绝不通声暂歇。别有幽愁暗恨生，此时无声胜有声。银瓶乍破水浆迸，铁骑突出刀枪鸣。曲终收拨当心画，四弦一声如裂帛。东船西舫悄无言，唯见江心秋月白。沉吟放拨插弦中，整顿衣裳起敛容。自言本是京城女，家在虾蟆陵下住。十三学得琵琶成，名属教坊第一部。曲罢曾教善才服，妆成每被秋娘妒。五陵年少争缠头，一曲红绡不知数。钿头银篦击节碎，血色罗裙翻酒污。今年欢笑复明年，秋月春风等闲度。弟走从军阿姨死，暮去朝来颜色故。门前冷落鞍马稀，老大嫁作商人妇。商人重利轻别离，前月浮梁买茶去。去来江口守空船，绕船月明江水寒。夜深忽梦少年事，梦啼妆泪红阑干。我闻琵琶已叹息，又闻此语重唧唧。同是天涯沦落人，相逢何必曾相识！我从去年辞帝京，谪居卧病浔阳城。浔阳地僻无音乐，终岁不闻丝竹声。住近湓江地低湿，黄芦苦竹绕宅生。其间旦暮闻何物？杜鹃啼血猿哀鸣。春江花朝秋月夜，往往取酒还独倾。岂无山歌与村笛？呕哑嘲哳难为听。今夜闻君琵琶语，如听仙乐耳暂明。莫辞更坐弹一曲，为君翻作《琵琶行》。感我此言良久立，却坐促弦弦转急。凄凄不似向前声，满座重闻皆掩泣。座中泣下谁最多？江州司马青衫湿。";

        int rangeHead = ostr.indexOf("琵琶");
        if (-1 != rangeHead) {
            String fstr = ostr.substring(0, rangeHead);
            System.out.println("第一处\"琵琶\"之前 : "+fstr);
        }

        // 从末尾向开头 查找第一处
        int rangeTail = ostr.lastIndexOf("琵琶");
        if (-1 != rangeTail) {
            String tstr = ostr.substring(rangeTail);
            System.out.println("最后一处\"琵琶\"之后 : "+tstr);
        }

        // 替换所有命中的 琵琶 为 PiPa
        String rpStr = ostr.replaceAll("琵琶", "PiPa");
        System.out.println("替换所有 琵琶 为 PiPa : "+rpStr);
    }

    // 正则查询替换
    protected static void regSearch(String ostr, String pstr) {
        //pattern 对象是一个正则表达式的编译表示。Pattern 类没有公共构造方法。要创建一个 Pattern 对象，你必须首先调用其公共静态编译方法，它返回一个 Pattern 对象。该方法接受一个正则表达式作为它的第一个参数。
        //Matcher 对象是对输入字符串进行解释和匹配操作的引擎。与Pattern 类一样，Matcher 也没有公共构造方法。你需要调用 Pattern 对象的 matcher 方法来获得一个 Matcher 对象。

        Pattern pt = Pattern.compile(pstr);

        Matcher mt = pt.matcher(ostr);
        while (mt.find()) {
            int gCount = mt.groupCount();
            System.out.println("正则匹配 groupCount = "+gCount);

            for (int i=0; i<=gCount; ++i) {
                System.out.println("正则匹配分组 "+i+", "+mt.group(i)+", start:"+mt.start(i)+", end:"+mt.end(i));
            }
        }
//        else {
//            System.out.println("正则匹配为空 "+pt.pattern());
//        }
    }

    // 正则替换
    protected static void regPlace(String ostr, String pstr, String replace2be) {
        Pattern pt = Pattern.compile(pstr);
        Matcher mt = pt.matcher(ostr);

        String rpStr = mt.replaceFirst(replace2be);
        System.out.println("正则匹配替换第一处 : "+rpStr);

        String rpStrAll = mt.replaceAll(replace2be);
        System.out.println("正则匹配替换所有 :   "+rpStrAll);
    }

    protected static void regSearchReplace() {

        // https://www.runoob.com/java/java-regular-expressions.html
        String ostr = "百科熊猫词条  https://baike.baidu.com/item/%E5%A4%A7%E7%86%8A%E7%8C%AB/34935?fromtitle=%E7%86%8A%E7%8C%AB&fromid=162918&fr=aladdin http://127.0.0.1:8080/dokuwiki/doku.php?id=cpp-str2arr panda@163.com 大熊猫（学名：Ailuropoda melanoleuca）：属于食肉目、熊科、大熊猫亚科和大熊猫属唯一的哺乳动物，头躯长1.2-1.8米，尾长10-12厘米。体重80-120千克，最重可达180千克，体色为黑白两色，它有着圆圆的脸颊，大大的黑眼圈，胖嘟嘟的身体，标志性的内八字的行走方式，也有解剖刀般锋利的爪子。是世界上最可爱的动物之一。  panda@gmail.com  com.panda@gmail (010)88886666 ，或 022-22334455，或 02912345678 511122198812068932 41112219881206893X 18660000062 17000000026 ";

        // 身份证
        String idPattern = "\\b(\\d{17}|\\d{14})(\\d|[xX])\\b";

        // 电话号码：eg：021-68686868  0511-6868686；
        String phonePattern = "\\b(\\d{3,4}-)\\d{7,8}\\b";

        // 手机
        String mobilePhonePattern = "\\b1\\d{10}\\b";

        // 邮箱
        String emailPattern = "\\b[a-zA-Z0-9]\\w*@[a-zA-Z0-9]+\\.[a-zA-Z]{2,4}\\b";

        String urlPattern = "(https?|ftp|file)://[-A-Za-z0-9+&@#/%?=~_|!:,.;]+[-A-Za-z0-9+&@#/%=~_|]";

        regSearch(ostr, idPattern);
        regSearch(ostr, phonePattern);
        regSearch(ostr, mobilePhonePattern);
        regSearch(ostr, emailPattern);
        regSearch(ostr, urlPattern);

        regPlace(ostr, idPattern, "____ID____");
        regPlace(ostr, phonePattern, "____Phone____");
        regPlace(ostr, mobilePhonePattern, "____Mobile____");
        regPlace(ostr, emailPattern, "____Email____");
        regPlace(ostr, urlPattern, "____URL____");
    }

    // 左trim
    protected static String leftTrim(String ostr) {
        // 使用正则替换
        String trimmed = ostr.replaceAll("^\\s+", "");
        return trimmed;
    }

    // 右trim
    protected static String rightTrim(String ostr) {
        // 使用正则替换
        String trimmed = ostr.replaceAll("\\s+$", "");
        return trimmed;
    }

    protected static void trimDemo() {
        String ostr = " \t 2sapce+1tab 山外青山楼外楼 3space   ";
        // 原生的两端trim
        String trimstr = ostr.trim();
        String regTrim = leftTrim(rightTrim(ostr));
        String leftTrimstr = leftTrim(ostr);
        String rightTrimstr = rightTrim(ostr);

        System.out.println("ostr        :|"+ostr+"|");
        System.out.println("leftTrimstr :|"+leftTrimstr+"|\t <-- 使用正则替换左起的空白字符");
        System.out.println("rightTrimstr:|"+rightTrimstr+"|\t <-- 使用正则替换右起的空白字符");
        System.out.println("regTrim     :|"+regTrim+"|\t\t <-- 使用正则替换左起和右起的空白字符");
        System.out.println("trimstr     :|"+trimstr+"|\t\t <-- 使用String类 原生的 trim api");

    }

    // 字符串格式化
    protected static void formatDemo() {
        // https://www.cnblogs.com/satire/p/16426550.html
        // https://www.cnblogs.com/HOI-Yzy/p/14671806.html

        /*

转换符	说  明	                            示  例
%b、%B	格式化为布尔类型	                    false
%h、%H	格式化为散列码	                        A05A5198
%s、%S	格式化为字符串类型	                    "abc"
%c、%C	格式化为字符类型	                    'w'
%d	    格式化为十进制数	                    26
%0	    格式化为八进制整数	                    12
%x、%X	格式化为十六进制整数	                4b 1
%e	    格式化为用计算机科学计数法表示的十进制数	1.700000e+01
%a	    格式化为带有效位数和指数的十六进制浮点值	0X1.C000000000001P4
%n	    结果为特定于平台的行分隔符
%%	    结果为字面值％，	                    %

搭配转换符的标志
标    志 说    明           示    例          结    果

+        为正数或者负数添加符号        ("%+d",15)               +15
−        左对齐                     ("%-5d",15)              |15   |
0        数字前面补0                 ("%04d", 99)             0099
空格      在整数之前添加指定数量的空格   ("% 4d", 99)             |  99|
,        以“,”对数字分组              ("%,f", 9999.99)        9,999.990000
(        使用括号包含负数             ("%(f", -99.99)          (99.990000)
<       格式化前一个转换符所描述的参数   ("%f和%<3.2f", 99.45)    99.450000和99.45
$       被格式化的参数索引             ("%1𝑑,s", 99,"abc")      99,abc
#       如果是浮点数则包含小数点，如果是16进制或8进制则添加0x或0 ("%#x", 99) ("%#o", 99) 0x63    0143
        */

        String str = String.format("Hi,%s", "王力");
        System.out.println(str);
        str = String.format("Hi,%s:%s.%s", "王南", "王力", "王张");
        System.out.println(str);
        System.out.printf("字母a的大写是：%c %n", 'A');
        System.out.printf("3>7的结果是：%b %n", 3 > 7);
        System.out.printf("100的一半是：%d %n", 100 / 2);
        System.out.printf("100的16进制数是：%x %n", 100);
        System.out.printf("100的8进制数是：%o %n", 100);
        System.out.printf("50元的书打8.5折扣是：%f 元%n", 50 * 0.85);
        System.out.printf("上面价格的16进制数是：%a %n", 50 * 0.85);
        System.out.printf("上面价格的指数表示：%e %n", 50 * 0.85);
        System.out.printf("上面价格的指数和浮点数结果的长度较短的是：%g %n", 50 * 0.85);
        System.out.printf("上面的折扣是%d%% %n", 85);
        System.out.printf("字母A的散列码是：%h %n", 'A');

        //$使用
        str=String.format("格式参数$的使用：%2$s,%1$d", 99,"abc");
        System.out.println(str);
        //+使用
        System.out.printf("显示正负数的符号：%+d与%d%n", 99,-99);
        //补O使用
        System.out.printf("最牛的编号是：%03d%n", 7);
        //空格使用
        System.out.printf("Tab键的效果是：% 8d%n", 7);
        //,使用
        System.out.printf("整数分组的效果是：%,d%n", 9989997);
        //空格和小数点后面个数
        System.out.printf("一本书的价格是：% 15.5f元%n", 49.8);
    }

    protected static void demoLogic() {
        // 字符串连接
        DateTimeDemo.logTs("stringJoin");
        stringJoin();

        // 字符串数组连接(components) 返回字符串
        DateTimeDemo.logTs("array2String");
        array2String();

        // 字符串分割(split) 返回字符串数组
        DateTimeDemo.logTs("string2Array");
        string2Array();

        // 字符串简单查询替换
        DateTimeDemo.logTs("subStringSearchReplace");
        subStringSearchReplace();

        // 正则查询替换
        DateTimeDemo.logTs("regSearchReplace");
        regSearchReplace();

        // 字符串 trim
        DateTimeDemo.logTs("trimDemo");
        trimDemo();

        // 字符串格式化
        DateTimeDemo.logTs("formatDemo");
        formatDemo();
    }
}
